package com.turkcell.dv.interceptor;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.text.ParseException;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import com.turkcell.dv.model.UserDOM;
import com.turkcell.dv.service.IdentityManagementService;
import com.turkcell.dv.service.RequestScopedContext;


public class DVInterceptor extends HandlerInterceptorAdapter {
	@Autowired
	private IdentityManagementService identityManagementService;
	
	@Autowired
	private RequestScopedContext rsc;
	
	private final String sharedKey = "a0a2abd8616241c383d61cf559b46afc";
	private final String ISSUER = "TURKCELL";

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
	
		String username = null;
		String password = null;
		String token = null;

		boolean pass = false;
		String authHdr = request.getHeader("Authorization");

		try {
			//If Basic Authentication
			if (authHdr != null && authHdr.length() > 5 && authHdr.substring(0, 5).equalsIgnoreCase("BASIC")) {
				byte[] encodedString = Base64.decodeBase64(authHdr.substring(5).getBytes());
				Set<String> attemptedAuths = new HashSet<String>(ENCODINGS.length * 2);
				for (String encoding : ENCODINGS) {
					CharsetDecoder decoder = Charset.forName(encoding).newDecoder().onMalformedInput(CodingErrorAction.REPORT);
					String basicAuth = decoder.decode(ByteBuffer.wrap(encodedString)).toString();
					if (!attemptedAuths.add(basicAuth)) {
						// Already tried - no need to try again
						continue;
					}
					int pos = basicAuth.indexOf(":");
					if (pos != -1) {
						username = basicAuth.substring(0, pos);
						password = basicAuth.substring(pos + 1);
					} else {
						username = basicAuth;
						password = "";
					}
					
					
					if (identityManagementService.checkUserAuthorization(username, request.getRequestURI())) {
						UserDOM user = identityManagementService.getUserByUsername(username);
						JWSSigner signer = new MACSigner(sharedKey);
						JWTClaimsSet claimsSet = new JWTClaimsSet();
						claimsSet.setIssueTime(new Date());
						claimsSet.setIssuer(ISSUER);
						claimsSet.setClaim("username", user.getUsername());
						claimsSet.setClaim("userId", Long.valueOf(user.getId()));
						rsc.setUsername(user.getUsername());
						rsc.setUserId(Long.valueOf(user.getId()));
						SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
						signedJWT.sign(signer);
						token = signedJWT.serialize();
						pass = true;
						System.out.println("Serialised JWS object: " + token);
						
					}
				}
			} else if (authHdr != null && authHdr.length() > 5 && authHdr.substring(0, 6).equalsIgnoreCase("Bearer")) {
				token = authHdr.substring(7, authHdr.length());
				SignedJWT signedJWT = SignedJWT.parse(token);
				JWSVerifier verifier = new MACVerifier(sharedKey);
				rsc.setUsername((String)signedJWT.getJWTClaimsSet().getClaim("username"));
				rsc.setUserId((Long)signedJWT.getJWTClaimsSet().getClaim("userId"));
				pass = signedJWT.verify(verifier);
			}
			if (pass && token != null) {
				response.setHeader("Authorization", "Bearer " + token);
				return true;
			}
		} catch (JOSEException  e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return false;
	}
	private static final String[] ENCODINGS;
	static {
		String[] encodings = new String[] { "UTF-8", System.getProperty("file.encoding"), "ISO-8859-1" };

		Set<String> encodingsSet = new LinkedHashSet<String>();
		for (String encoding : encodings) {
			encodingsSet.add(encoding);
		}
		ENCODINGS = new String[encodingsSet.size()];
		encodingsSet.toArray(ENCODINGS);
	}
}
